using System;
using System.IO;
using System.Xml.Serialization;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using EnvDTE;

namespace JTLeigh.Tools.Development.CopySourceAsHtml.UnitTests
{

    /// <summary>
    /// Unit tests for the <see cref="ColorableItemStyle"/> class.
    /// </summary>
    [TestClass]
    public class ColorableItemStyleFixture
    {
        #region Inner Classes and Types

        private class TestColorableItem : ColorableItems
        {
            public TestColorableItem(uint foregroundColor, uint backgroundColor, bool bold)
            {
                Foreground = foregroundColor;
                Background = backgroundColor;
                Bold = bold;
            }

            #region ColorableItems Members

            public uint Foreground { get; set; }

            public uint Background { get; set; }

            public bool Bold { get; set; }

            public string Name
            {
                get { throw new NotImplementedException(); }
            }

            #endregion
        }

        #endregion // Inner Classes and Types

        #region Methods

        #region Constructors

        /// <summary>
        /// Ensures the <see cref="ColorableItemStyle"/> constructor correctly throws <see cref="ArgumentNullException"/>
        /// when called with a <see langword="null"/> foreground color.
        /// </summary>
        [TestMethod]
        [ExpectedException(typeof(ArgumentNullException))]
        public void TestConstructorWithNullForegroundColor()
        {
            ColorableItemStyle parentStyle;

            parentStyle = new ColorableItemStyle(new HtmlColor(0, 0, 0), new HtmlColor(255, 255, 255), true);
            new ColorableItemStyle(null, new HtmlColor(255, 255, 255), false, parentStyle);
        }

        /// <summary>
        /// Ensures the <see cref="ColorableItemStyle"/> constructor correctly throws <see cref="ArgumentNullException"/>
        /// when called with a <see langword="null"/> background color.
        /// </summary>
        [TestMethod]
        [ExpectedException(typeof(ArgumentNullException))]
        public void TestConstructorWithNullBackgroundColor()
        {
            ColorableItemStyle parentStyle;

            parentStyle = new ColorableItemStyle(new HtmlColor(0, 0, 0), new HtmlColor(255, 255, 255), true);
            new ColorableItemStyle(new HtmlColor(0, 0, 0), null, false, parentStyle);
        }

        /// <summary>
        /// Ensures the <see cref="ColorableItemStyle"/> constructor sets the new instance's 
        /// properties correctly when no parent style is provided.
        /// </summary>
        [TestMethod]
        public void TestConstructorNoParentStyle()
        {
            ColorableItemStyle style;

            style = new ColorableItemStyle(new HtmlColor(0, 0, 0), new HtmlColor(255, 255, 255), true);
            Assert.AreEqual(new HtmlColor(0, 0, 0), style.ForegroundColor);
            Assert.AreEqual(new HtmlColor(255, 255, 255), style.BackgroundColor);
            Assert.IsTrue(style.Bold);
            Assert.AreEqual("color: black; background: white; font-weight: bold;", style.ToString());

            style = new ColorableItemStyle(new HtmlColor(0, 0, 0), new HtmlColor(255, 255, 255), false);
            Assert.AreEqual(new HtmlColor(0, 0, 0), style.ForegroundColor);
            Assert.AreEqual(new HtmlColor(255, 255, 255), style.BackgroundColor);
            Assert.IsFalse(style.Bold);
            Assert.AreEqual("color: black; background: white;", style.ToString());
        }

        /// <summary>
        /// Ensures the <see cref="ColorableItemStyle"/> constructor sets the new instance's 
        /// properties correctly when a parent style is provided and the foreground color, 
        /// background color, and bold value match the parent style.
        /// </summary>
        [TestMethod]
        public void TestConstructorWithParentStyleInheritingEverything()
        {
            ColorableItemStyle parentStyle;
            ColorableItemStyle style;

            parentStyle = new ColorableItemStyle(new HtmlColor(0, 0, 0), new HtmlColor(255, 255, 255), true);

            style = new ColorableItemStyle(new HtmlColor(0, 0, 0), new HtmlColor(255, 255, 255), true, parentStyle);
            Assert.AreEqual("", style.ToString());
        }

        /// <summary>
        /// Ensures the <see cref="ColorableItemStyle"/> constructor sets the new instance's 
        /// properties correctly when a parent style is provided and the foreground color does
        /// not match the parent style.
        /// </summary>
        [TestMethod]
        public void TestConstructorWithParentStyleOverridingForegroundColor()
        {
            ColorableItemStyle parentStyle;
            ColorableItemStyle style;

            parentStyle = new ColorableItemStyle(new HtmlColor(0, 0, 0), new HtmlColor(255, 255, 255), true);

            style = new ColorableItemStyle(new HtmlColor(255, 0, 0), new HtmlColor(255, 255, 255), true, parentStyle);
            Assert.AreEqual("color: red;", style.ToString());
        }

        /// <summary>
        /// Ensures the <see cref="ColorableItemStyle"/> constructor sets the new instance's 
        /// properties correctly when a parent style is provided and the background color does
        /// not match the parent style.
        /// </summary>
        [TestMethod]
        public void TestConstructorWithParentStyleOverridingBackgroundColor()
        {
            ColorableItemStyle parentStyle;
            ColorableItemStyle style;

            parentStyle = new ColorableItemStyle(new HtmlColor(0, 0, 0), new HtmlColor(255, 255, 255), true);

            style = new ColorableItemStyle(new HtmlColor(0, 0, 0), new HtmlColor(255, 0, 0), true, parentStyle);
            Assert.AreEqual("background: red;", style.ToString());
        }

        /// <summary>
        /// Ensures the <see cref="ColorableItemStyle"/> constructor sets the new instance's 
        /// properties correctly when a parent style is provided and the bold value does not match
        /// the parent style.
        /// </summary>
        [TestMethod]
        public void TestConstructorWithParentStyleOverridingBold()
        {
            ColorableItemStyle parentStyle;
            ColorableItemStyle style;

            // parent bold, child not bold, must set font weight back to normal.
            parentStyle = new ColorableItemStyle(new HtmlColor(0, 0, 0), new HtmlColor(255, 255, 255), true);
            style = new ColorableItemStyle(new HtmlColor(0, 0, 0), new HtmlColor(255, 255, 255), false, parentStyle);
            Assert.AreEqual("font-weight: normal;", style.ToString());

            // parent not bold, child bold, must set font weight to bold.
            parentStyle = new ColorableItemStyle(new HtmlColor(0, 0, 0), new HtmlColor(255, 255, 255), false);
            style = new ColorableItemStyle(new HtmlColor(0, 0, 0), new HtmlColor(255, 255, 255), true, parentStyle);
            Assert.AreEqual("font-weight: bold;", style.ToString());
        }

        /// <summary>
        /// Ensures the <see cref="ColorableItemStyle"/> constructor sets the new instance's 
        /// properties correctly when a <see cref="ColorableItems"/> object is provided but no
        /// parent style is provided.
        /// </summary>
        [TestMethod]
        public void TestConstructorWithColorableItemsNoParentStyle()
        {
            ColorableItems colorItem;
            ColorableItemStyle style;

            colorItem = new TestColorableItem(0, 255, true);
            style = new ColorableItemStyle(colorItem);

            Assert.AreEqual(new HtmlColor(0, 0, 0), style.ForegroundColor);
            Assert.AreEqual(new HtmlColor(255, 0, 0), style.BackgroundColor);
            Assert.IsTrue(style.Bold);
            Assert.AreEqual("color: black; background: red; font-weight: bold;", style.ToString());
        }

        /// <summary>
        /// Ensures the <see cref="ColorableItemStyle"/> constructor sets the new instance's 
        /// properties correctly when both a <see cref="ColorableItems"/> object and a parent style
        /// is provided.
        /// </summary>
        [TestMethod]
        public void TestConstructorWithColorableItemsParentStyle()
        {
            ColorableItemStyle parentStyle;
            ColorableItems colorItem;
            ColorableItemStyle style;

            parentStyle = new ColorableItemStyle(new HtmlColor(0, 0, 0), new HtmlColor(255, 0, 0), true);

            colorItem = new TestColorableItem(0, 255, true);
            style = new ColorableItemStyle(colorItem, parentStyle);

            Assert.AreEqual(new HtmlColor(0, 0, 0), style.ForegroundColor);
            Assert.AreEqual(new HtmlColor(255, 0, 0), style.BackgroundColor);
            Assert.IsTrue(style.Bold);
            Assert.AreEqual("", style.ToString());
        }

        #endregion // Constructors

        #region Serialization

        /// <summary>
        /// Ensures <see cref="ColorableItemStyle"/> can be serialized to and deserialized from
        /// XML with no loss of data.
        /// </summary>
        [TestMethod]
        public void TestSerializeDeserialize()
        {
            XmlSerializer serializer;
            MemoryStream stream;
            ColorableItemStyle originalStyle;
            ColorableItemStyle copiedStyle;

            serializer = new XmlSerializer(typeof(ColorableItemStyle));
            stream = new MemoryStream();
            
            originalStyle = new ColorableItemStyle(new HtmlColor(0, 0, 0), new HtmlColor(255, 255, 255), true);
            originalStyle.Append("margin: 0px;");

            serializer.Serialize(Console.Out, originalStyle);
            serializer.Serialize(stream, originalStyle);
            stream.Position = 0;
            copiedStyle = (ColorableItemStyle)serializer.Deserialize(stream);

            Assert.AreEqual(originalStyle.ForegroundColor, copiedStyle.ForegroundColor);
            Assert.AreEqual(originalStyle.BackgroundColor, copiedStyle.BackgroundColor);
            Assert.AreEqual(originalStyle.Bold, copiedStyle.Bold);
            Assert.IsNull(copiedStyle.ParentStyle);

            Assert.AreEqual(originalStyle.ToString(), copiedStyle.ToString());
        }

        /// <summary>
        /// Ensures a <see cref="ColorableItemStyle"/> with a parent style can be serialized to and 
        /// deserialized from XML with no loss of data.
        /// </summary>
        [TestMethod]
        public void TestSerializeDeserializeWithParentStyle()
        {
            XmlSerializer serializer;
            MemoryStream stream;
            ColorableItemStyle originalParentStyle;
            ColorableItemStyle originalStyle;
            ColorableItemStyle copiedStyle;

            serializer = new XmlSerializer(typeof(ColorableItemStyle));
            stream = new MemoryStream();

            originalParentStyle = new ColorableItemStyle(new HtmlColor(255, 0, 0), new HtmlColor(128, 128, 128), false);
            originalStyle = new ColorableItemStyle(new HtmlColor(0, 0, 0), new HtmlColor(255, 255, 255), true, originalParentStyle);
            originalStyle.Append("margin: 0px;");

            serializer.Serialize(Console.Out, originalStyle);
            serializer.Serialize(stream, originalStyle);
            stream.Position = 0;
            copiedStyle = (ColorableItemStyle)serializer.Deserialize(stream);

            Assert.AreEqual(originalStyle.ForegroundColor, copiedStyle.ForegroundColor);
            Assert.AreEqual(originalStyle.BackgroundColor, copiedStyle.BackgroundColor);
            Assert.AreEqual(originalStyle.Bold, copiedStyle.Bold);
            Assert.IsNotNull(copiedStyle.ParentStyle);

            Assert.AreEqual(originalParentStyle.ForegroundColor, copiedStyle.ParentStyle.ForegroundColor);
            Assert.AreEqual(originalParentStyle.BackgroundColor, copiedStyle.ParentStyle.BackgroundColor);
            Assert.AreEqual(originalParentStyle.Bold, copiedStyle.ParentStyle.Bold);

            Assert.AreEqual(originalStyle.ToString(), copiedStyle.ToString());
        }

        #endregion // Serialization

        #endregion // Methods
    }

}
